﻿// this code is from the Microsoft Silverlight toolkit (System.Windows.Navigation.PageResourceContentLoader)
// and is used here because functions we need are not publicly accessible.
//-----------------------------------------------------------------------
// <copyright company="Microsoft">
//      (c) Copyright Microsoft Corporation.
//      This source is subject to the Microsoft Public License (Ms-PL).
//      Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
//      All other rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Resources;

namespace BHB.Library.Navigation
{
    /// <summary>
    /// a series of static functions useful when loading a page from a URI.  pulled from
    /// System.Windows.Navigation.PageResourceContentLoader
    /// </summary>
    public class PageLoader
    {
        #region Static fields and constants

        private static readonly Regex xClassRegex = new Regex(".*x:Class=\"(.*?)\"", RegexOptions.CultureInvariant);

        #endregion

        #region Methods

        /// <summary>
        /// Returns the XAML string of the page at the given path.
        /// If that page cannot be found (path does not exist, etc.)
        /// then it returns null.
        /// </summary>
        /// <param name="pagePathAndName">The path and name of the XAML (with the ".xaml" included)</param>
        /// <returns>See summary</returns>
        private static string GetLocalXaml(string pagePathAndName)
        {
            string assemblyPartSource = null;
            string pagePathAndNameWithoutAssembly = null;

            if (!pagePathAndName.Contains(";component/"))
            {
                assemblyPartSource = GetEntryPointAssemblyPartSource();
                pagePathAndNameWithoutAssembly = pagePathAndName;
            }
            else
            {
                string[] pagePathAndNameParts = pagePathAndName.Split(new string[] { ";component" }, StringSplitOptions.RemoveEmptyEntries);
                if (pagePathAndNameParts.Length != 2)
                {
                    throw new InvalidOperationException("PageResourceContentLoader_InvalidComponentSyntax");
                }
                assemblyPartSource = pagePathAndNameParts[0];
                pagePathAndNameWithoutAssembly = pagePathAndNameParts[1];
            }

            if (String.IsNullOrEmpty(assemblyPartSource))
            {
                return null;
            }

            // In case the Uri contains international characters (ex: 完全采用统.xaml), we need to escape them.
            // because Application.GetResourceStream expects an escaped Uri.
            pagePathAndNameWithoutAssembly = Uri.EscapeUriString(pagePathAndNameWithoutAssembly);
            assemblyPartSource = Uri.EscapeUriString(assemblyPartSource);

            StreamResourceInfo sri = Application.GetResourceStream(new Uri(assemblyPartSource + ";component" + pagePathAndNameWithoutAssembly, UriKind.Relative));
            if (sri == null)
            {
                return null;
            }

            using (StreamReader reader = new StreamReader(sri.Stream))
            {
                return reader.ReadToEnd();
            }
        }

        private static string GetEntryPointAssemblyPartSource()
        {
            string assemblyPartSource = null;

            foreach (AssemblyPart ap in Deployment.Current.Parts)
            {
                if (ap.Source.Substring(0, ap.Source.Length - 4) == Deployment.Current.EntryPointAssembly)
                {
                    assemblyPartSource = ap.Source;
                    break;
                }
            }

            return assemblyPartSource.Substring(0, assemblyPartSource.Length - 4);
        }

        private static string GetXClass(string xaml)
        {
            Match m = xClassRegex.Match(xaml);

            if (m == Match.Empty)
            {
                return null;
            }
            else
            {
                return m.Groups[1].Value;
            }
        }

        private static Type GetTypeFromAnyLoadedAssembly(string typeName)
        {
            Type t = null;

            foreach (AssemblyPart ap in Deployment.Current.Parts)
            {
                StreamResourceInfo sri = Application.GetResourceStream(new Uri(ap.Source, UriKind.Relative));
                if (sri != null)
                {
                    Assembly theAssembly = new AssemblyPart().Load(sri.Stream);
                    if (theAssembly != null)
                    {
                        t = Type.GetType(
                            typeName + "," + theAssembly,
                            false /* don't throw on error, just return null */);
                    }
                }
                if (t != null)
                {
                    break;
                }
            }

            return t;
        }

        /// <summary>
        /// gets the type of a page from its URI.
        /// </summary>
        /// <param name="uri">the URI of the page whose type you wish to get</param>
        /// <returns>if the page can be loaded. The type is returned. If the page is not able to be loaded, null is returned</returns>
        public static Type GetTypeFromUri(Uri uri)
        {
            string localXaml = GetLocalXaml(uri.ToString());
            if (localXaml == null) return null;
            string xClass = GetXClass(localXaml);
            
            return xClass == null ? null : GetTypeFromAnyLoadedAssembly(xClass);
        }
        #endregion
    }
}